﻿namespace SharpF2.ModelView
{
	using System;
	using System.Collections.Generic;
	using System.Collections.ObjectModel;
	using System.ComponentModel;
	using System.Globalization;
	using System.Linq;
	using System.Windows.Input;
	using Commands;
	using Model;

	public class IdentifiersViewModel : ViewModelBase
	{
		#region Events

		public event EventHandler UpdateFileItemsEvent;

		#endregion

		#region Constructors

		public IdentifiersViewModel()
		{
			SelectedIdentifierItems = new ObservableCollection<IdentifierItem>();
			currentDefaultIdentifierItem = IdentifierItem.GetIdentifierItem("");
		}

		#endregion

		#region Public Attributes

		public Boolean workingInBackground;

		#endregion

		#region Private Attributes

		private DelegateCommand addReplaceFilterCommand;
		private IdentifierItem currentDefaultIdentifierItem;
		private ObservableCollection<ReplaceFilter> itemsReplaceFilters = new ObservableCollection<ReplaceFilter>();
		private DelegateCommand<ReplaceFilter> removeReplaceFilterCommand;
		private DelegateCommand resetCommand;

		private Boolean selectedIdentifierCapitalizeWords;
		private Boolean selectedIdentifierFirstLetterUpperCase;

		private ObservableCollection<IdentifierItem> selectedIdentifierItems;
		private Boolean selectedIdentifierLowerCase;
		private Boolean selectedIdentifierNoChange;
		private string selectedIdentifierPattern;
		private string selectedIdentifierReplacement;
		private Boolean selectedIdentifierUpperCase;
		private DelegateCommand setAsDefaultCommand;
		private ObservableCollection<IdentifierItem> tempSelectedIdentifierItems;

		#endregion

		#region Public Properties

		public ICommand AddReplaceFilterCommand
		{
			get
			{
				if (null == addReplaceFilterCommand)
					addReplaceFilterCommand = new DelegateCommand(ExecuteAddReplaceFilterCommand, CanExecuteAddReplaceFilterCommand);
				return addReplaceFilterCommand;
			}
		}

		public Boolean HasSelectedIdentifier
		{
			get
			{
				if (null == SelectedIdentifierItems)
					return false;
				return SelectedIdentifierItems.Count() > 0;
			}
		}

		public Boolean NotWorkingInBackground { get; set; }

		public ICommand RemoveReplaceFilterCommand
		{
			get
			{
				if (null == removeReplaceFilterCommand)
					removeReplaceFilterCommand = new DelegateCommand<ReplaceFilter>(ExecuteRemoveReplaceFilterCommand);
				return removeReplaceFilterCommand;
			}
		}

		public ICommand ResetCommand
		{
			get
			{
				if (null == resetCommand)
					resetCommand = new DelegateCommand(ExecuteResetCommand, CanExecuteResetCommand);
				return resetCommand;
			}
		}

		public ObservableCollection<IdentifierItem> SelectedIdentifierItems
		{
			get { return selectedIdentifierItems; }
			set
			{
				selectedIdentifierItems = value;

				SelectedIdentifierItemsChangedUpdateCaseFilter();
				UpdateReplaceFilters();

				OnPropertyChanged("SelectedIdentifierItems");
				OnPropertyChanged("SelectedIdentifiersIsAnInteger");
				OnPropertyChanged("SelectedIdentifiersIntegerLength");
				OnPropertyChanged("HasSelectedIdentifier");
			}
		}

		public Boolean SelectedIdentifiersCapitalizeWords
		{
			get { return selectedIdentifierCapitalizeWords; }
			set
			{
				selectedIdentifierCapitalizeWords = value;
				SetSelectedIdentifierItemsCaseFilter(CaseFilterEnums.CapitalizeWords);
				UpdateFileItems();
				OnPropertyChanged("SelectedIdentifiersCapitalizeWords");
			}
		}

		public Boolean SelectedIdentifiersFirstLetterUpperCase
		{
			get { return selectedIdentifierFirstLetterUpperCase; }
			set
			{
				selectedIdentifierFirstLetterUpperCase = value;
				SetSelectedIdentifierItemsCaseFilter(CaseFilterEnums.FirstLetterUpperCase);
				UpdateFileItems();
				OnPropertyChanged("SelectedIdentifiersFirstLetterUpperCase");
			}
		}

		public String SelectedIdentifiersIntegerLength
		{
			get
			{
				if (null == SelectedIdentifierItems || 0 == SelectedIdentifierItems.Count || null == SelectedIdentifierItems.First())
					return String.Empty;

				return SelectedIdentifierItems.All(
				       	i => i.IntegerLength == SelectedIdentifierItems.First().IntegerLength)
				       	? SelectedIdentifierItems.First().IntegerLength.ToString()
				       	: String.Empty;
			}
			set
			{
				UInt32 integerLength;
				if (UInt32.TryParse(value, NumberStyles.None, CultureInfo.CurrentCulture, out integerLength))
					foreach (var identifierItem in SelectedIdentifierItems)
						identifierItem.IntegerLength = integerLength;
				UpdateFileItems();
				OnPropertyChanged("SelectedIdentifiersIntegerLength");
			}
		}

		public Boolean SelectedIdentifiersIsAnInteger
		{
			get { return null != SelectedIdentifierItems && SelectedIdentifierItems.All(i => i.IsAnInteger); }
			set
			{
				if (null == SelectedIdentifierItems) return;
				foreach (var identifierItem in SelectedIdentifierItems)
					identifierItem.IsAnInteger = value;
				UpdateFileItems();
				OnPropertyChanged("SelectedIdentifiersIsAnInteger");
			}
		}

		public Boolean SelectedIdentifiersLowerCase
		{
			get { return selectedIdentifierLowerCase; }
			set
			{
				selectedIdentifierLowerCase = value;
				SetSelectedIdentifierItemsCaseFilter(CaseFilterEnums.LowerCase);
				UpdateFileItems();
				OnPropertyChanged("SelectedIdentifiersLowerCase");
			}
		}

		public Boolean SelectedIdentifiersNoChange
		{
			get { return selectedIdentifierNoChange; }
			set
			{
				selectedIdentifierNoChange = value;
				SetSelectedIdentifierItemsCaseFilter(CaseFilterEnums.NoChange);
				UpdateFileItems();
				OnPropertyChanged("SelectedIdentifiersNoChange");
			}
		}


		public String SelectedIdentifiersPattern
		{
			get { return selectedIdentifierPattern; }
			set
			{
				if (selectedIdentifierPattern == value)
					return;
				selectedIdentifierPattern = value;
				OnPropertyChanged("SelectedIdentifiersPattern");
				OnPropertyChanged("HasSelectedIdentifier");
			}
		}

		public ObservableCollection<ReplaceFilter> SelectedIdentifiersReplaceFilters
		{
			get { return itemsReplaceFilters; }
			set
			{
				itemsReplaceFilters = value;
				OnPropertyChanged("SelectedIdentifiersReplaceFilters");
			}
		}

		public String SelectedIdentifiersReplacement
		{
			get { return selectedIdentifierReplacement; }
			set
			{
				if (selectedIdentifierReplacement == value)
					return;
				selectedIdentifierReplacement = value;
				OnPropertyChanged("SelectedIdentifiersReplacement");
			}
		}

		public Boolean SelectedIdentifiersUpperCase
		{
			get { return selectedIdentifierUpperCase; }
			set
			{
				selectedIdentifierUpperCase = value;
				SetSelectedIdentifierItemsCaseFilter(CaseFilterEnums.UpperCase);
				UpdateFileItems();
				OnPropertyChanged("SelectedIdentifiersUpperCase");
			}
		}

		public ICommand SetAsDefaultCommand
		{
			get
			{
				if (null == setAsDefaultCommand)
					setAsDefaultCommand = new DelegateCommand(ExecuteSetAsDefaultCommand, CanExecuteSetAsDefaultCommand);
				return setAsDefaultCommand;
			}
		}

		public Boolean WorkingInBackground
		{
			get { return workingInBackground; }
			set
			{
				workingInBackground = value;
				NotWorkingInBackground = !value;

				DelegateCommandUtility.RaiseCanExecuteChanged(addReplaceFilterCommand);
				DelegateCommandUtility.RaiseCanExecuteChanged(removeReplaceFilterCommand);

				OnPropertyChanged("WorkingInBackground");
				OnPropertyChanged("NotWorkingInBackground");
			}
		}

		#endregion

		#region Public Members

		public void BackupSelection()
		{
			tempSelectedIdentifierItems = SelectedIdentifierItems;
		}

		public void LimitSelection(IEnumerable<IdentifierItem> newCollection)
		{
			var nextCollection = from currentItem in tempSelectedIdentifierItems
			                     join newItem in newCollection on currentItem.IdentifierId equals newItem.IdentifierId
			                     select newItem;
			SelectedIdentifierItems = new ObservableCollection<IdentifierItem>(nextCollection);
		}

		#endregion

		#region Private Members

		/// <summary>
		/// Return true if equal
		/// </summary>
		/// <param name="a"></param>
		/// <param name="b"></param>
		/// <returns></returns>
		private static bool CompareIdentifierItem(IdentifierItem a, IdentifierItem b)
		{
			var equal = a.CaseFilter == b.CaseFilter
			            && a.IsAnInteger == b.IsAnInteger
			            && a.IntegerLength == b.IntegerLength;

			if (false == equal)
				return false;

			if (a.ReplaceFilters.Count != b.ReplaceFilters.Count)
				return false;

			var array1 = a.ReplaceFilters.ToArray();
			Array.Sort(array1);

			var array2 = b.ReplaceFilters.ToArray();
			Array.Sort(array2);

			for (var i = 0; i < array1.Count(); i++)
				if (0 != array1[i].CompareTo(array2[i]))
					return false;

			return true;
		}

		private bool CanExecuteAddReplaceFilterCommand()
		{
			if (String.IsNullOrEmpty(SelectedIdentifiersPattern))
				return false;

			foreach (var item in SelectedIdentifierItems)
			{
				foreach (var filter in item.ReplaceFilters)
				{
					if (filter.Pattern.Equals(SelectedIdentifiersPattern))
						return false;
				}
			}
			return true;
		}

		private bool CanExecuteResetCommand()
		{
			if (SelectedIdentifierItems.Count == 0)
				return false;

			foreach (var item in SelectedIdentifierItems)
				if (!CompareIdentifierItem(item, currentDefaultIdentifierItem))
					return true;

			return false;
		}

		private bool CanExecuteSetAsDefaultCommand()
		{
			if (SelectedIdentifierItems.Count == 0)
				return false;

			if (SelectedIdentifierItems.Count == 1)
				return !CompareIdentifierItem(SelectedIdentifierItems[0], currentDefaultIdentifierItem);

			var o = SelectedIdentifierItems[0];
			var b = SelectedIdentifierItems.All(i => CompareIdentifierItem(i, o));

			if (false == b)
				return false;

			return !CompareIdentifierItem(o, currentDefaultIdentifierItem);
		}

		private void DefaultSelectedIdentifiersCaseFilter()
		{
			selectedIdentifierNoChange = false;
			selectedIdentifierLowerCase = false;
			selectedIdentifierUpperCase = false;
			selectedIdentifierFirstLetterUpperCase = false;
			selectedIdentifierCapitalizeWords = false;

			OnPropertyChanged("SelectedIdentifiersNoChange");
			OnPropertyChanged("SelectedIdentifiersLowerCase");
			OnPropertyChanged("SelectedIdentifiersUpperCase");
			OnPropertyChanged("SelectedIdentifiersFirstLetterUpperCase");
			OnPropertyChanged("SelectedIdentifiersCapitalizeWords");
		}

		private void ExecuteAddReplaceFilterCommand()
		{
			if (null == SelectedIdentifierItems) return;
			foreach (var identifierItem in SelectedIdentifierItems)
			{
				var filter = new ReplaceFilter(SelectedIdentifiersPattern, SelectedIdentifiersReplacement);
				filter.PropertyChanged += FilterPropertyChanged;
				identifierItem.AddReplaceFilter(filter);
			}
			UpdateReplaceFilters();
			UpdateFileItems();
		}

		private void ExecuteRemoveReplaceFilterCommand(ReplaceFilter o)
		{
			if (null == SelectedIdentifierItems) return;
			foreach (var identifierItem in SelectedIdentifierItems)
			{
				o.PropertyChanged -= FilterPropertyChanged;
				identifierItem.RemoveReplaceFilter(o);
			}
			UpdateReplaceFilters();
			UpdateFileItems();
		}

		private void ExecuteResetCommand()
		{
			if (SelectedIdentifierItems.Count == 0)
				return;

			var item = IdentifierItem.GetIdentifierItem("");

			foreach (var identifierItem in SelectedIdentifierItems)
			{
				identifierItem.CaseFilter = item.CaseFilter;
				identifierItem.IntegerLength = item.IntegerLength;
				identifierItem.IsAnInteger = item.IsAnInteger;
				identifierItem.ReplaceFilters = new ObservableCollection<ReplaceFilter>(item.ReplaceFilters);
			}

			SelectedIdentifierItemsChangedUpdateCaseFilter();
			UpdateReplaceFilters();
			OnPropertyChanged("SelectedIdentifierItems");
			OnPropertyChanged("SelectedIdentifiersIsAnInteger");
			OnPropertyChanged("SelectedIdentifiersIntegerLength");
			OnPropertyChanged("HasSelectedIdentifier");
		}

		private void ExecuteSetAsDefaultCommand()
		{
			if (SelectedIdentifierItems.Count == 0)
				return;

			var item = SelectedIdentifierItems[0];
			IdentifierItem.SetIdentifierItem(item);
			currentDefaultIdentifierItem = new IdentifierItem(item) {IdentifierId = String.Empty};
		}

		private void FilterPropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			UpdateFileItems();
		}

		private void SelectedIdentifierItemsChangedUpdateCaseFilter()
		{
			if (null == SelectedIdentifierItems || 0 == SelectedIdentifierItems.Count()) return;

			var caseFilter = SelectedIdentifierItems.First().CaseFilter;

			if (SelectedIdentifierItems.All(i => i.CaseFilter == caseFilter))
				UpdateSelectedIdentifiersCaseFilter(caseFilter);
			else
				DefaultSelectedIdentifiersCaseFilter();
		}

		private void SelectedIdentifiersReplaceFilters_PropertyChanged(object sender, PropertyChangedEventArgs e)
		{
			var replaceFilter = sender as ReplaceFilter;
			if (null == replaceFilter) return;

			foreach (var replaceFilterToUnsubscribe in SelectedIdentifiersReplaceFilters)
				replaceFilterToUnsubscribe.PropertyChanged -= SelectedIdentifiersReplaceFilters_PropertyChanged;

			if ("Pattern".Equals(e.PropertyName))
			{
				foreach (var item in SelectedIdentifierItems)
					foreach (var filter in item.ReplaceFilters)
						if (filter != replaceFilter &&
						    filter.Pattern == replaceFilter.LastPattern)
							filter.Pattern = replaceFilter.Pattern;
			}
			else if ("Replacement".Equals(e.PropertyName))
			{
				foreach (var item in SelectedIdentifierItems)
					foreach (var filter in item.ReplaceFilters)
						if (filter != replaceFilter &&
						    filter.Pattern == replaceFilter.Pattern &&
						    filter.Replacement == replaceFilter.LastReplacement)
							filter.Replacement = replaceFilter.Replacement;
			}

			foreach (var replaceFilterToResubscribe in SelectedIdentifiersReplaceFilters)
				replaceFilterToResubscribe.PropertyChanged += SelectedIdentifiersReplaceFilters_PropertyChanged;

			UpdateFileItems();
		}

		private void SetSelectedIdentifierItemsCaseFilter(CaseFilterEnums caseFilter)
		{
			foreach (var identifierItem in SelectedIdentifierItems)
				identifierItem.CaseFilter = caseFilter;

			UpdateSelectedIdentifiersCaseFilter(caseFilter);
		}

		private void UpdateFileItems()
		{
			if (null != UpdateFileItemsEvent)
				UpdateFileItemsEvent(this, EventArgs.Empty);
		}

		private void UpdateReplaceFilters()
		{
			foreach (var replaceFilter in SelectedIdentifiersReplaceFilters)
				replaceFilter.PropertyChanged -= SelectedIdentifiersReplaceFilters_PropertyChanged;

			SelectedIdentifiersReplaceFilters.Clear();

			var replaceFiltersEnumerator = SelectedIdentifierItems.GetEnumerator();
			if (!replaceFiltersEnumerator.MoveNext()) return;

			var replaceFiltersReserve = replaceFiltersEnumerator.Current.ReplaceFilters.ToList();

			while (replaceFiltersEnumerator.MoveNext())
				replaceFiltersReserve = new List<ReplaceFilter>(
					replaceFiltersEnumerator.Current.ReplaceFilters.Intersect(replaceFiltersReserve));

			SelectedIdentifiersReplaceFilters = new ObservableCollection<ReplaceFilter>(replaceFiltersReserve);

			foreach (var replaceFilter in SelectedIdentifiersReplaceFilters)
				replaceFilter.PropertyChanged += SelectedIdentifiersReplaceFilters_PropertyChanged;
		}

		private void UpdateSelectedIdentifiersCaseFilter(CaseFilterEnums caseFilter)
		{
			selectedIdentifierNoChange = false;
			selectedIdentifierLowerCase = false;
			selectedIdentifierUpperCase = false;
			selectedIdentifierFirstLetterUpperCase = false;
			selectedIdentifierCapitalizeWords = false;
			switch (caseFilter)
			{
				case CaseFilterEnums.NoChange:
					selectedIdentifierNoChange = true;
					break;
				case CaseFilterEnums.LowerCase:
					selectedIdentifierLowerCase = true;
					break;
				case CaseFilterEnums.UpperCase:
					selectedIdentifierUpperCase = true;
					break;
				case CaseFilterEnums.FirstLetterUpperCase:
					selectedIdentifierFirstLetterUpperCase = true;
					break;
				case CaseFilterEnums.CapitalizeWords:
					selectedIdentifierCapitalizeWords = true;
					break;
				default:
					break;
			}

			OnPropertyChanged("SelectedIdentifiersNoChange");
			OnPropertyChanged("SelectedIdentifiersLowerCase");
			OnPropertyChanged("SelectedIdentifiersUpperCase");
			OnPropertyChanged("SelectedIdentifiersFirstLetterUpperCase");
			OnPropertyChanged("SelectedIdentifiersCapitalizeWords");
		}

		#endregion
	}
}